﻿using System;
using System.Windows.Media;
using System.Windows.Media.Media3D;
using Perspective.Wpf3D.Primitives;
using System.Windows;

namespace Perspective.Wpf3D.Sculptors
{
    /// <summary>
    /// 组成凹棱柱的所有点和三角形集合
    /// A class to handle points and triangles of a 3D concave prism model.
    /// Default radius is 1.0.
    /// By default, the direction of the prism is the Z axis, and the length is 1.0.
    /// </summary>
    public class ConcavePrismSculptor : Sculptor
    {
        #region Constant
        internal const double DefaultCircumcircleRadius = 1.0;
        internal const double DefaultHeight = 1.0;
        internal const double DefaultConcaveRadius = 0.2;
        #endregion

        #region Members
        private int _circumferenceBranchCount;
        private double _initialAngle;
        private double _circumcircleRadius = DefaultCircumcircleRadius;
        private double _concaveRadius = DefaultConcaveRadius;
        private double _height = DefaultHeight;
        private double _roundingRate;
        private ConcavePolygonSculptor _cps1;
        private ConcavePolygonSculptor _cps2;
        #endregion


        #region Properties

         #region Constructors
        /// <summary>
        /// Initializes a new instance of ConcavePrismSculptor.
        /// </summary>
        public ConcavePrismSculptor()
        {
            //TextureOnSide = true;
            TexturePosition = TexturePositions.OnSides;
        }

        /// <summary>
        /// Initializes a new instance of ConcavePrismSculptor.
        /// </summary>
        /// <param name="circumferenceBranchCount">Model circumference branch count.</param>
        /// <param name="initialAngle">Angle between the axis [origin - first point] and the X-axis, in degrees.</param>
        public ConcavePrismSculptor(int circumferenceBranchCount, double initialAngle)
        {
            Initialize(circumferenceBranchCount, initialAngle);
        }

        /// <summary>
        /// Initializes a new instance of ConcavePrismSculptor.
        /// </summary>
        /// <param name="circumcircleRadius">Circumcircle Radius</param>
        /// <param name="height">Model height.</param>
        /// <param name="circumferenceSideCount">Model circumference side count.</param>
        /// <param name="initialAngle">Angle between the axis [origin - first point] and the X-axis, in degrees.</param>
        /// <param name="concaveRadius">Concave Radius</param>
        public ConcavePrismSculptor(double circumcircleRadius, double height, int circumferenceSideCount, double initialAngle, double concaveRadius)
        {
            Initialize(circumcircleRadius, height, circumferenceSideCount, initialAngle, concaveRadius);
        }

        /// <summary>
        /// Initializes an existing instance of PrismSculptor.
        /// </summary>
        /// <param name="circumferenceSideCount">Model circumference side count.</param>
        /// <param name="initialAngle">Angle between the axis [origin - first point] and the X-axis, in degrees.</param>
        public void Initialize(int circumferenceBranchCount, double initialAngle)
        {
            Initialize(DefaultCircumcircleRadius, DefaultHeight, circumferenceBranchCount, initialAngle,DefaultConcaveRadius);
        }

        /// <summary>
        /// Initializes an existing instance of PrismSculptor.
        /// </summary>
        /// <param name="circumcircleRadius">Circumcircle Radius</param>
        /// <param name="height">Model height.</param>
        /// <param name="circumferenceBranchCount">Model circumference bramch count.</param>
        /// <param name="initialAngle">Angle between the axis [origin - first point] and the X-axis, in degrees.</param>
        /// <param name="concaveRadius">Concave Radius</param>
        public void Initialize(double circumcircleRadius, double height, int circumferenceBranchCount, double initialAngle, double concaveRadius)
        {
            _circumferenceBranchCount = circumferenceBranchCount;
            _initialAngle = initialAngle;
            _circumcircleRadius = circumcircleRadius;
            _concaveRadius = concaveRadius;
            _height = height;
        }
        #endregion

        #region ConcaveRadius 凹点圆半径

        /// <summary>
        /// Gets or sets the radius of the concave circle.
        /// </summary>
        protected double ConcaveRadius
        {
            get { return _concaveRadius; }
            set { _concaveRadius = value; }
        }

        #endregion

        #region TexturePosition 纹理坐标位置

        /// <summary>
        /// Gets or sets the position a a texture. Default is OnSides.
        /// </summary>
        public TexturePositions TexturePosition { get; set; }

        #endregion

        #region PolygonSculptor1 下表面多边形

        /// <summary>
        /// Gets the 1st ConcavePolygonSculptor.
        /// </summary>
        public ConcavePolygonSculptor ConcavePolygonSculptor1
        {
            get { return _cps1; }
        }

        #endregion

        #region PolygonSculptor2 上表面多边形

        /// <summary>
        /// Gets the 2nd ConcavePolygonSculptor.
        /// </summary>
        public ConcavePolygonSculptor ConcavePolygonSculptor2
        {
            get { return _cps2; }
        }

        #endregion

        #endregion

        #region Functions

        /// <summary>
        /// Initializes the Points and Triangles collections.
        /// Called By Sculptor.BuildMesh()
        /// </summary>
        protected override void CreateTriangles()
        {
            _cps1 = new ConcavePolygonSculptor(_circumcircleRadius, _circumferenceBranchCount, _initialAngle, _concaveRadius);
            //_cps1.RoundingRate = _roundingRate;
            _cps1.BuildTriangles(TriangleSideKind.Back);
            foreach (Point3D p in _cps1.Points)
            {
                this.Points.Add(p);
            }
            foreach (Point3DTriplet tpl in _cps1.Triangles)
            {
                this.Triangles.Add(tpl);
            }

            _cps2 = new ConcavePolygonSculptor(_circumcircleRadius, _circumferenceBranchCount, _initialAngle, _concaveRadius);
            _cps2.Center = new Point3D(
                _cps2.Center.X,
                _cps2.Center.Y,
                _cps2.Center.Z + _height);
            for (int i = 0; i < _cps2.Points.Count; i++)
            {
                _cps2.Points[i] = new Point3D(
                    _cps2.Points[i].X,
                    _cps2.Points[i].Y,
                    _cps2.Points[i].Z + _height);
                this.Points.Add(_cps2.Points[i]);
            }
            //_cps2.RoundingRate = _roundingRate;
            _cps2.BuildTriangles(TriangleSideKind.Front);

            foreach (Point3DTriplet tpl in _cps2.Triangles)
            {
                this.Triangles.Add(tpl);
            }

            for (int i = 0; i < _cps1.Points.Count; i++)
            {
                if (i < _cps1.Points.Count - 1)
                {
                    this.Triangles.Add(new Point3DTriplet(_cps1.Points[i], _cps2.Points[i + 1], _cps2.Points[i]));
                    this.Triangles.Add(new Point3DTriplet(_cps1.Points[i], _cps1.Points[i + 1], _cps2.Points[i + 1]));
                }
                else
                {
                    this.Triangles.Add(new Point3DTriplet(_cps1.Points[i], _cps2.Points[0], _cps2.Points[i]));
                    this.Triangles.Add(new Point3DTriplet(_cps1.Points[i], _cps1.Points[0], _cps2.Points[0]));
                }
            }
        }

        /// <summary>
        /// A method for building the TextureCoordinates collection of the mesh.
        /// </summary>
        internal override void MapTexture()
        {
            Mesh.TextureCoordinates.Clear();
            if (TexturePosition == TexturePositions.OnEnds)
            {
                foreach (Point3DTriplet pt in _cps1.Triangles)
                {
                    foreach (Point3D p in pt.Points)
                    {
                        Mesh.TextureCoordinates.Add(
                            new Point(p.X, -p.Y));
                    }
                }
                foreach (Point3DTriplet pt in _cps2.Triangles)
                {
                    foreach (Point3D p in pt.Points)
                    {
                        Mesh.TextureCoordinates.Add(
                            new Point(p.X, -p.Y));
                    }
                }
            }
            else if (TexturePosition == TexturePositions.OnSides)
            {
                double minY = 0.0;
                double maxY = 1.0;
                double minX = 0.0;
                double maxX;
                double xPos = 0.0;
                double segmentLength = 1.0;

                foreach (Point3DTriplet pt in _cps1.Triangles)
                {
                    foreach (Point3D p in pt.Points)
                    {
                        Mesh.TextureCoordinates.Add(new Point(0.0, 0.0));
                    }
                }

                foreach (Point3DTriplet pt in _cps2.Triangles)
                {
                    foreach (Point3D p in pt.Points)
                    {
                        Mesh.TextureCoordinates.Add(new Point(0.0, 0.0));
                    }
                }

                for (int i = 0; i <= _cps1.Points.Count - 1; i++)
                {
                    if (_roundingRate != 0.0)
                    {
                        Point3D p2;
                        if (i == _cps1.Points.Count - 1)
                        {
                            p2 = _cps1.Points[0];
                        }
                        else
                        {
                            p2 = _cps1.Points[i + 1];
                        }
                        Vector3D v = p2 - _cps1.Points[i];
                        segmentLength = v.Length;
                    }
                    minX = xPos;
                    xPos += segmentLength;
                    maxX = xPos;

                    Mesh.TextureCoordinates.Add(new Point(minX, maxY));
                    Mesh.TextureCoordinates.Add(new Point(maxX, minY));
                    Mesh.TextureCoordinates.Add(new Point(minX, minY));

                    Mesh.TextureCoordinates.Add(new Point(minX, maxY));
                    Mesh.TextureCoordinates.Add(new Point(maxX, maxY));
                    Mesh.TextureCoordinates.Add(new Point(maxX, minY));
                }
            }
        }

        #endregion
    }
}
